/*
   Copyright (C) 2007 Yen-Ju Chen <yjchenx gmail>

   This application is free software; you can redistribute it and/or 
   modify it under the terms of the MIT license. See COPYING.

*/

#import "PubMedFactory.h"
#import "PDObject.h"

@implementation PubMedFactory
/* Private */
- (PDObject *) _objectFromPubMed: (NSString *) xml
{
	PDObject *object = [[PDObject alloc] initWithXML: xml];
	return AUTORELEASE(object);
}

- (void) _loadDatabaseAtPath: (NSString *) p
{
	NSLog(@"%@ %@", self, NSStringFromSelector(_cmd));
	NSString *error = nil;
	NSPropertyListFormat format = 0;
	NSData *data = [NSData dataWithContentsOfFile: p];
	id propertyList= [NSPropertyListSerialization propertyListFromData: data
	                               mutabilityOption: NSPropertyListImmutable
	                                         format: &format
	                               errorDescription: &error];
	if (propertyList== nil)
	{
		NSLog(@"Error: %@ (%@ %@)", error, self, NSStringFromSelector(_cmd));
	}
	ASSIGN(_rootGroup, [OKObject objectWithPropertyList: propertyList]);
}


- (void) _savePubMed: (id) sender
{
	NSLog(@"%@ %@", self, NSStringFromSelector(_cmd));
	/* Save root group */
	NSString *error = nil;
	id propertyList = [_rootGroup propertyList];
	NSData *data = [NSPropertyListSerialization dataFromPropertyList: propertyList 
	                                format: NSPropertyListXMLFormat_v1_0
	                                            errorDescription: &error];
	if (data == nil)
	{
		NSLog(@"Error: %@ (%@ %@)", error, self, NSStringFromSelector(_cmd));
	}
	[data writeToFile: _savePath atomically: YES];

	/* Saved */
	_isSavable = NO;
}

- (void) _refreshPubMed: (id) sender
{
	NSLog(@"%@ %@", self, NSStringFromSelector(_cmd));
	/* Build up root group */
	ASSIGN(_rootGroup, AUTORELEASE([[OKGroup alloc] init]));

	NSEnumerator *e = [[fm directoryContentsAtPath: _xmlPath] objectEnumerator];
	NSString *p = nil, *file = nil;
	BOOL isDir = NO;
	while ((file = [e nextObject]))
	{
		if ([[file pathExtension] isEqualToString: @"xml"] == NO)
			continue;

		p = [_xmlPath stringByAppendingPathComponent: file];

		if ([fm fileExistsAtPath: p isDirectory: &isDir] && (isDir == NO))
		{
			PDObject *object = [self _objectFromPubMed: p];

			if (object)
			{
				p = [_paperPath stringByAppendingPathComponent: file];
				p = [p stringByDeletingPathExtension];
				p = [p stringByAppendingPathExtension: @"pdf"];
				if ([fm fileExistsAtPath: p isDirectory: &isDir] &&
				    (isDir == NO))
				{
					[object setValue: p forProperty: kPubMedPDFFileProperty];
				}
				[_rootGroup addObject: object];
			}
		}
	}

	[_rootGroup setValue: @"PubMed" forProperty: kOKGroupNameProperty];

	[[NSNotificationCenter defaultCenter] 
	              postNotificationName: KRFactoryDidUpdateNotification
	              object: self];

	/* Refreshed, can be saved */
	_needsRefresh = NO;
	_isSavable = YES;
}

- (void) _applicationWillTerminate: (NSNotification *) not
{
	if (_isSavable)
		[self _savePubMed: _savePath];
}

- (void) _displayObjectInContentView: (OKObject *) object
{
	NSAttributedString *as = nil;
	NSDictionary *attr = nil;
	NSString *text = nil;
	NSTextStorage *textStorage = [_textView textStorage];

	[textStorage beginEditing];

	[textStorage deleteCharactersInRange: NSMakeRange(0, [textStorage length])];

	/* Title */
	text = [object valueForProperty: kPubMedTitleProperty];
	if (text)
	{
		attr = [NSDictionary dictionaryWithObjectsAndKeys:
			[NSFont boldSystemFontOfSize: 16], NSFontAttributeName,
			nil];
		as = [[NSAttributedString alloc] initWithString: text
		                                     attributes: attr];
		[textStorage appendAttributedString: as];
		DESTROY(as);

		as = [[NSAttributedString alloc] initWithString: @"\n\n"];
		[textStorage appendAttributedString: as];
		DESTROY(as);
	}

	/* Abstract */
	text = [object valueForProperty: kPubMedAbstractProperty];
	if (text)
	{
		attr = [NSDictionary dictionaryWithObjectsAndKeys:
			[NSFont userFontOfSize: 12], NSFontAttributeName,
			nil];
		as = [[NSAttributedString alloc] initWithString: text
		                                     attributes: attr];
		[textStorage appendAttributedString: as];
		DESTROY(as);
	}

	[textStorage endEditing];
}

/* End of Private */
- (void) didSelectObject: (OKObject *) object
{
	[self _displayObjectInContentView: object];
}

- (NSView *) contentView
{
	if (_scrollView == nil)
	{
		NSRect rect = NSMakeRect(0, 0, 100, 100);
		_scrollView = [[NSScrollView alloc] initWithFrame: rect];
		[_scrollView setHasVerticalScroller: YES];
		[_scrollView setAutoresizesSubviews: YES];
		[_scrollView setAutoresizingMask: NSViewWidthSizable |
		                                  NSViewHeightSizable];
		_textView = [[NSTextView alloc] initWithFrame: rect];
		[_textView setAutoresizingMask: NSViewWidthSizable |
		                                NSViewHeightSizable];
		[_textView setEditable: NO];
		[_scrollView setDocumentView: _textView];
	}
	return _scrollView;
}

- (OKGroup *) rootGroup
{
	return _rootGroup;
}

- (NSArray *) propertiesToDisplay
{
	if (_propertiesToDisplay == nil)
	{
		_propertiesToDisplay = [[NSArray alloc] initWithObjects:
			kPubMedTitleProperty,
			kPubMedYearProperty,
			kPubMedJournalProperty,
			kPubMedPMIDProperty,
			nil];
	}
	return _propertiesToDisplay;
}

- (unsigned int) viewableMask
{
	return KRFactoryItemViewableMask | KRFactoryContentViewableMask;
}

- (NSArray *) collectionMenuItems
{
	if (_collectionMenuItems == nil)
	{
		_collectionMenuItems = [[NSMutableArray alloc] init];
		id <NSMenuItem> item = [[NSMenuItem alloc] initWithTitle: @"Refresh PubMed"
		                                           action: @selector(_refreshPubMed:)
		                                           keyEquivalent: @""];
		[item setTarget: self];
		[_collectionMenuItems addObject: item];
		DESTROY(item);
	}
	return _collectionMenuItems;
}

/* NSMenu */

- (BOOL) validateMenuItem: (id <NSMenuItem>) menuItem
{
#if 0
	if ([menuItem action] == @selector(_refreshPubMed:))
	{
		return _needsRefresh;
	}
#endif
	return YES;
}

/* NSObject */
- (id) init
{
	self = [super init];

	_isSavable = NO;
	_needsRefresh = NO;

	fm = [NSFileManager defaultManager];
	ASSIGN(_xmlPath, 
	       ([NSString pathWithComponents: [NSArray arrayWithObjects:
	       NSHomeDirectory(), @"Documents", @"iPapers", @"cache", nil]]));
	ASSIGN(_paperPath,
	       ([NSString pathWithComponents: [NSArray arrayWithObjects: 
	       NSHomeDirectory(), @"Documents", @"iPapers", @"iPapers paper",
	       nil]]));
	ASSIGN(_savePath, 
	       ([NSString pathWithComponents: [NSArray arrayWithObjects:
		   NSHomeDirectory(), @"Documents", @"iPapers", @"Koelr", nil]]));
	BOOL isDir = NO;
	if ([fm fileExistsAtPath: _xmlPath isDirectory: &isDir] == NO ||
	    (isDir == NO))
	{
		NSLog(@"No ipapers cache at %@", _xmlPath);
		[self dealloc];
		return nil;
	}
	if ([fm fileExistsAtPath: _paperPath isDirectory: &isDir] == NO ||
        (isDir == NO))
	{
		NSLog(@"No ipapers files at %@", _paperPath);
		[self dealloc];
		return nil;
	}

	/* Do we have previously saved data ? */
	if ([fm fileExistsAtPath: _savePath isDirectory: &isDir] == NO)
	{
		[self _refreshPubMed: self];
		_isSavable = YES; // Allow saving since we have no database 
	}
	else
	{
		if (isDir == YES)
		{
			NSLog(@"Koelr database is corrupted. Please delete it at %@", _savePath);
			[self dealloc];
			return nil;
		}
		/* Compare the modification date (_savePath) to PubMed database (xmlPath)*/
		NSDate *kDate = [[fm fileAttributesAtPath: _savePath traverseLink: YES] objectForKey: NSFileModificationDate];
		NSDate *xDate = [[fm fileAttributesAtPath: _xmlPath traverseLink: YES] objectForKey: NSFileModificationDate];
		if ([kDate compare: xDate] == NSOrderedAscending)
		{
			_needsRefresh = YES;
			[self _refreshPubMed: self];
			_isSavable = YES;
		}
		else
		{
			[self _loadDatabaseAtPath: _savePath];
		}
	}

	/* Listen to application termination */
	[[NSNotificationCenter defaultCenter] addObserver: self
	                          selector: @selector(_applicationWillTerminate:)
	                              name: NSApplicationWillTerminateNotification
	                            object: nil];
	
	return self;
}

- (void) dealloc
{
	DESTROY(_scrollView);
	DESTROY(_textView);
	DESTROY(_rootGroup);
	DESTROY(_propertiesToDisplay);
	[super dealloc];
}

@end

